package org.fhm.zdte.server.cmd;

import org.fhm.substrate.annotation.Component;
import org.fhm.substrate.annotation.Setup;
import org.fhm.substrate.service.LoggerHandler;
import org.fhm.substrate.standard.ILogger;
import org.fhm.zdte.common.ability.IIsOffLineByIdentification;
import org.fhm.zdte.common.ability.IRefreshBaseTime;
import org.fhm.zdte.common.pojo.Address;
import org.fhm.zdte.common.protocol.AbstractMessageStandard;
import org.fhm.zdte.common.standard.IReceiveMessageManager;
import org.fhm.zdte.common.standard.ISendMessageManager;
import org.fhm.zdte.common.util.CommonUtil;

import java.util.ArrayList;
import java.util.Map;
import java.util.Objects;

/**
 * Online-Check command processor.
 *
 * @author 谭波
 * @since 2023/11/9
 */
@Component("ONLINE_CHECK")
public class OlineCheckServerCmd extends AbstractServerCmd implements IRefreshBaseTime, IIsOffLineByIdentification {

    private final ILogger logger = LoggerHandler.getLogger(OlineCheckServerCmd.class);

    @Setup
    private CommonUtil commonUtil;

    @Setup
    private Map<String, ISendMessageManager> sendMessageManagerContainer;

    @Setup
    private Map<String, IReceiveMessageManager> receiveMessageManagerContainer;

    @Override
    public void receiveCmd(Address address, AbstractMessageStandard message) {
        if (abstractMessageMonitor.isAck(message))
            refreshBaseTime(message.getFrom());
    }

    @Override
    public void sendCmd(Address address, AbstractMessageStandard message) {
        for (String from : new ArrayList<>(onlineUserContainer.keySet())) {
            iChannelDispatcher.obtainChannelManager(address)
                    .sendMessage(message, onlineUserContainer.get(from));
            Long time = onlineTimeRecordContainer.get(from);
            if ((time + (serverInitialConfiguration.getOnlineCheckTimeout() * 1000L)) < commonUtil.getCurTimeMillisSecond()) {
                sendMessageManagerContainer
                        .values()
                        .forEach(
                                manager -> {
                                    if (Objects.nonNull(onlineUserContainer.get(from))) {
                                        synchronized (from) {
                                            if (Objects.nonNull(onlineUserContainer.get(from))) {
                                                manager.clearMessageBuffer(from);
                                            }
                                        }
                                    }
                                }
                        );
                receiveMessageManagerContainer
                        .values()
                        .forEach(
                                manager -> {
                                    if (Objects.nonNull(onlineUserContainer.get(from))) {
                                        synchronized (from) {
                                            if (Objects.nonNull(onlineUserContainer.get(from))) {
                                                manager.clearMessageBuffer(from);
                                            }
                                        }
                                    }
                                }
                        );
                onlineTimeRecordContainer.remove(from);
                onlineUserContainer.remove(from);
                logger.info("the user with the identification is {} has been taken offline", from);
            }
        }
    }

    @Override
    public void refreshBaseTime(String from) {
        Long time = onlineTimeRecordContainer.get(from);
        if (Objects.nonNull(time)) {
            synchronized (time) {
                if (Objects.nonNull(onlineTimeRecordContainer.get(from)))
                    onlineTimeRecordContainer.put(from, commonUtil.getCurTimeMillisSecond());
            }
        }
    }

    @Override
    public Boolean isOffLineByIdentification(String from) {
        return Objects.isNull(onlineUserContainer.get(from));
    }
}
